﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Controls;
using System.Windows.Data;

using Chronicle.Analyzation;
using Chronicle.Parser;

namespace Chronicle.Modules
{
	class HealModule : AnalyzeModule
	{
		AnalyzeVisitor visitor;
		
		public override string Name {
			get {
				return "Heal";
			}
		}

		#region Event analyzation
		class HealTag
		{
			public long TotalHeal;
			public Dictionary<string, SpellData> Heal     = new Dictionary<string, SpellData> ();
			public Dictionary<string, SpellData> CritHeal = new Dictionary<string, SpellData> ();
		}

		public override void Attach (AnalyzeVisitor visitor)
		{
			this.visitor = visitor;
			visitor.EventHeal += HandleEventHeal;
		}

		void HandleEventHeal (object sender, EventEventArgs e)
		{
			var curEvent = visitor.curEvent;
			var eventHeal = e.Event;
			var actor = curEvent.GetActor(eventHeal);
			var tagData = actor.GetTagData (() => new HealTag());
			tagData.TotalHeal += eventHeal.Amount;
			SpellData data;
			var spellDataCollection = eventHeal.Critical ? tagData.CritHeal : tagData.Heal;
			if (!spellDataCollection.TryGetValue(e.Event.AbilityId, out data))
				spellDataCollection[e.Event.AbilityId] = data = new SpellData(e.Event.Ability, e.Event.AbilityId) { Min = e.Event.Amount, Max = e.Event.Amount };

			data.TotalAmount += eventHeal.Amount;
			data.Max = Math.Max (data.Max, eventHeal.Amount);
			data.Min = Math.Min (data.Min, eventHeal.Amount);
			data.Times++;
			actor.LastAction = eventHeal.TimeStamp;

			actor = visitor.allEvents.GetActor(eventHeal);
			tagData = actor.GetTagData (() => new HealTag());
			tagData.TotalHeal += eventHeal.Amount;
			spellDataCollection = eventHeal.Critical ? tagData.CritHeal : tagData.Heal;
			if (!spellDataCollection.TryGetValue(e.Event.AbilityId, out data))
				spellDataCollection[e.Event.AbilityId] = data = new SpellData(e.Event.Ability, e.Event.AbilityId) { Min = e.Event.Amount, Max = e.Event.Amount };

			data.TotalAmount += eventHeal.Amount;
			data.Max = Math.Max (data.Max, eventHeal.Amount);
			data.Min = Math.Min (data.Min, eventHeal.Amount);
			data.Times++;
		}
		#endregion

		public override int Compare (Actor left, Actor right)
		{
			return left.GetTagData (() => new HealTag()).TotalHeal.CompareTo (right.GetTagData (() => new HealTag()).TotalHeal);
		}
		
		public override string GetHintText(EventInformation eventInformation, Actor actor)
		{
			var battleSeconds = eventInformation.TotalSeconds == 0 ? (actor.LastAction - actor.FirstAction).TotalSeconds : eventInformation.TotalSeconds;
			var tagData = actor.GetTagData (() => new HealTag());
			var totalHeal = tagData.TotalHeal;
			var dps = battleSeconds > 0 ? (long)(totalHeal / battleSeconds) : 0;
			return totalHeal +  "(" + dps  + " hps)";
		}

		class TotalData
		{
			public string Name { get; set; }
			public long TotalHeal { get; set; }
			public long Heal { get; set; }
			public long CritHeal { get; set; }
			public long Min = long.MaxValue, Max = long.MinValue, Count = 0;
			public long CritMin = long.MaxValue, CritMax = long.MinValue, CritCount = 0;
			
			public long Percent { get; set; }
		}
		
		class AbilityContainer {
			public TotalData TotalData { get; set; }
			public string Name { get; set; }
			public long Heal { get; set; }
			public long Percent { get; set; }
		}
		
		class AbilityDetailContainer {
			public string Type  { get; set; }
			public long Min { get; set; }
			public long Avg { get; set; }
			public long Max { get; set; }
			public long Count { get; set; }
			public long Percent { get; set; }
			
			public AbilityDetailContainer(string type, long min, long avg, long max, long count, long percent)
			{
				this.Type = type;
				this.Min = min;
				this.Avg = avg;
				this.Max = max;
				this.Count = count;
				this.Percent = percent;
			}
		}
		
		public override void CreateDetailsView(ListView abilities, ListView details, Actor actor)
		{
			// collect data
			var tagData = actor.GetTagData (() => new HealTag());
			var normal = tagData.Heal;
			var crit = tagData.CritHeal;
			Dictionary<string, TotalData> collectedData = new Dictionary<string, TotalData> ();
			long totalHeal = 0;
			
			foreach (SpellData data in normal.Values) {
				totalHeal += data.TotalAmount;
				TotalData total = new TotalData () { 
					Heal = data.TotalAmount,
					Name = data.SpellName,
					Min = data.Min, 
					Max = data.Max,
					Count = data.Times};
				collectedData[data.SpellId] = total;
			}
			
			foreach (SpellData data in crit.Values) {
				totalHeal += data.TotalAmount;
				TotalData total;
				if (!collectedData.TryGetValue (data.SpellId, out total))
					collectedData[data.SpellId] = total = new TotalData () { Name = data.SpellName
				};
				total.CritHeal = data.TotalAmount;
				total.CritMin = data.Min;
				total.CritMax = data.Max;
				total.CritCount = data.Times;
			}
			// create views
			var abilityGrid = new GridView ();
			abilities.View = abilityGrid;
			abilityGrid.Columns.Add (new GridViewColumn () { Header = "Ability Name", DisplayMemberBinding = new Binding("Name")});
			abilityGrid.Columns.Add (new GridViewColumn () { Header = "Heal", DisplayMemberBinding = new Binding("Heal")});
			abilityGrid.Columns.Add (new GridViewColumn () { Header = "%", DisplayMemberBinding = new Binding("Percent")});
			
			abilities.ItemsSource = collectedData.Values.Select (totalData => {
			                                                     	long allDmg = totalData.Heal + totalData.CritHeal;
			                                                     	totalData.Percent = allDmg * 100 / totalHeal;
			                                                     	return new AbilityContainer () {
			                                                     		TotalData = totalData,
			                                                     		Name = totalData.Name,
			                                                     		Heal = allDmg,
			                                                     		Percent = totalData.Percent
			                                                     	};
			});
			
			var detailsGrid = new GridView ();
			details.View = detailsGrid;
			detailsGrid.Columns.Add (new GridViewColumn () { Header = "Type", DisplayMemberBinding = new Binding("Type")});
			detailsGrid.Columns.Add (new GridViewColumn () { Header = "Min", DisplayMemberBinding = new Binding("Min")});
			detailsGrid.Columns.Add (new GridViewColumn () { Header = "Avg", DisplayMemberBinding = new Binding("Avg")});
			detailsGrid.Columns.Add (new GridViewColumn () { Header = "Max", DisplayMemberBinding = new Binding("Max")});
			detailsGrid.Columns.Add (new GridViewColumn () { Header = "Count", DisplayMemberBinding = new Binding("Count")});
			detailsGrid.Columns.Add (new GridViewColumn () { Header = "%", DisplayMemberBinding = new Binding("Percent")});
			
			
			abilities.SelectionChanged += delegate(object sender, SelectionChangedEventArgs e) { 
				var d = (AbilityContainer)e.AddedItems[0];
				if (d == null)
					return;
				var list = new List<AbilityDetailContainer> ();
				
				var data = d.TotalData;
				long totalCount = data.Count + data.CritCount;
				if (data.Count > 0)
					list.Add (new AbilityDetailContainer ("Hit", data.Min, data.Heal / data.Count, data.Max, data.Count, data.Count * 100 / totalCount));
				if (data.CritCount > 0)
					list.Add (new AbilityDetailContainer ("Crit", data.CritMin, data.CritHeal / data.CritCount, data.CritMax, data.CritCount, data.CritCount * 100 / totalCount));
				details.ItemsSource = list;
			};
		}
/*
		public override void CreateDetailsView(TreeView treeviewAbilities, TreeView treeviewDetails, Actor actor)
		{
			TreeStore abilityStore = new TreeStore (typeof (string), typeof (long), typeof (long), typeof (TotalData));
			TreeStore detailsStore = new TreeStore (typeof (string), typeof (long), typeof (long), typeof (long), typeof (long), typeof (long));
			abilityStore.SetSortColumnId (1, SortType.Descending);
			treeviewAbilities.Model = abilityStore;
			var tagData = actor.GetTagData (() => new HealTag());
			var normal = tagData.Heal;
			var crit = tagData.CritHeal;
			Dictionary<string, TotalData> collectedData = new Dictionary<string, TotalData> ();
			long totalHeal = 0;
			
			foreach (SpellData data in normal.Values) {
				totalHeal += data.TotalAmount;
				TotalData total = new TotalData () { 
					Heal = data.TotalAmount,
					Name = data.SpellName,
					Min = data.Min, 
					Max = data.Max,
					Count = data.Times };
				collectedData[data.SpellId] = total;
			}
			
			foreach (SpellData data in crit.Values) {
				totalHeal += data.TotalAmount;
				TotalData total;
				if (!collectedData.TryGetValue (data.SpellId, out total))
					collectedData[data.SpellId] = total = new TotalData () { Name = data.SpellName };
				total.CritHeal = data.TotalAmount;
				total.CritMin = data.Min;
				total.CritMax = data.Max;
				total.CritCount = data.Times;
			}
			
			foreach (var totalData in collectedData.Values) {
				long allDmg = totalData.Heal + totalData.CritHeal;
				totalData.Percent = allDmg * 100 / totalHeal;
				abilityStore.AppendValues (totalData.Name, allDmg, totalData.Percent, totalData);
			}
			treeviewAbilities.AppendColumn ("Ability Name", new CellRendererText (), "text", 0);
			var textRenderer = new CellRendererText ();
			var col = treeviewAbilities.AppendColumn ("Heal", textRenderer);
			col.SetCellDataFunc (textRenderer, delegate (TreeViewColumn column, CellRenderer cell, TreeModel model, TreeIter iter) {
				var amount = (long)model.GetValue (iter, 1);
				((CellRendererText)cell).Text = amount.ToString ();
			});
			col = treeviewAbilities.AppendColumn ("%", textRenderer);
			col.SetCellDataFunc (textRenderer, delegate (TreeViewColumn column, CellRenderer cell, TreeModel model, TreeIter iter) {
				var amount = (long)model.GetValue (iter, 2);
				((CellRendererText)cell).Text = amount.ToString () + "%";
			});
			
			
			treeviewDetails.AppendColumn ("Type", new CellRendererText (), "text", 0);
			col = treeviewDetails.AppendColumn ("Min", textRenderer);
			col.SetCellDataFunc (textRenderer, delegate (TreeViewColumn column, CellRenderer cell, TreeModel model, TreeIter iter) {
				var amount = (long)model.GetValue (iter, 1);
				((CellRendererText)cell).Text = amount.ToString ();
			});
			col = treeviewDetails.AppendColumn ("Avg", textRenderer);
			col.SetCellDataFunc (textRenderer, delegate (TreeViewColumn column, CellRenderer cell, TreeModel model, TreeIter iter) {
				var amount = (long)model.GetValue (iter, 2);
				((CellRendererText)cell).Text = amount.ToString ();
			});
			col = treeviewDetails.AppendColumn ("Max", textRenderer);
			col.SetCellDataFunc (textRenderer, delegate (TreeViewColumn column, CellRenderer cell, TreeModel model, TreeIter iter) {
				var amount = (long)model.GetValue (iter, 3);
				((CellRendererText)cell).Text = amount.ToString ();
			});
			col = treeviewDetails.AppendColumn ("Count", textRenderer);
			col.SetCellDataFunc (textRenderer, delegate (TreeViewColumn column, CellRenderer cell, TreeModel model, TreeIter iter) {
				var amount = (long)model.GetValue (iter, 4);
				((CellRendererText)cell).Text = amount.ToString ();
			});
			col = treeviewDetails.AppendColumn ("%", textRenderer);
			col.SetCellDataFunc (textRenderer, delegate (TreeViewColumn column, CellRenderer cell, TreeModel model, TreeIter iter) {
				var amount = (long)model.GetValue (iter, 5);
				((CellRendererText)cell).Text = amount.ToString () + "%";
			});

			treeviewAbilities.Selection.Changed += delegate {
				detailsStore.Clear ();
				TreeIter iter;
				if (!treeviewAbilities.Selection.GetSelected (out iter))
					return;
				var data = (TotalData)abilityStore.GetValue (iter, 3);
				if (data.Count > 0)
					detailsStore.AppendValues ("Hit", data.Min, data.Heal / data.Count, data.Max, data.Count, data.Count * 100 / (data.Count + data.CritCount));
				if (data.CritCount > 0)
					detailsStore.AppendValues ("Crit", data.CritMin, data.CritHeal / data.CritCount, data.CritMax, data.CritCount, data.CritCount * 100 / (data.Count + data.CritCount));
				
			};
			treeviewDetails.Model = detailsStore;
		}*/
	}
}
